#include<string>
#include<iostream>
#include <fstream>
#include <vector>
#include<cmath>
#include"LargeNumber.h"
#include"NumArray.h"


LargeNumber::LargeNumber(NumArray const & num, bool is_negative) : number_(num), isNegative_(is_negative) {}

LargeNumber::LargeNumber(size_t const & size_): number_(NumArray(0)), isNegative_(false) {
    number_.resize(size_);
}

LargeNumber::LargeNumber(long const & value) {
    isNegative_ = (value < 0);
    number_ = NumArray( abs(value + 0.0) );
}

LargeNumber::LargeNumber(): number_(NumArray(0)), isNegative_(false) {}

LargeNumber & LargeNumber::operator+=(LargeNumber const & right) {
    if( isNegative_ == right.isNegative_ ){
        return plus(right);
    }
    return minus( right );
}

LargeNumber & LargeNumber::operator-= (LargeNumber const & right) {
    isNegative_ = !isNegative_;
    *this += right;
    isNegative_ = !isNegative_;
    return *this;
}


LargeNumber & LargeNumber::operator*=(LargeNumber const & right) {
    if(number_.isSmall() && right.number_.isSmall()) {
        unsigned long long first = number_[0];
        unsigned long long second = right.number_[0];
        unsigned long long result = first * second;
        isNegative_ = (isNegative_ ^ right.isNegative_);
        number_[0] = result;
        return * this;
    }
    if(number_.isSmall() != right.number_.isSmall()) {
        if(abs(*this) < abs(right)) {
            number_.normalize();
        } else {
            LargeNumber operand(right);
            operand.number_.normalize();
            return multiplication(operand);
        }
    }
    return multiplication(right);
}

LargeNumber & LargeNumber::multiplication(LargeNumber const & right) {
    LargeNumber result(right.number_.size() + number_.size());
    for(size_t i = 0; i < right.number_.size(); ++i) {
        LargeNumber line(number_.size() + i + 1);
        unsigned char remember = 0;
        for(size_t j = 0; j < number_.size(); ++j) {
            unsigned char res = number_[j] * right.number_[i] + remember;
            remember = res / 10;
            line.number_[j + i] = res % 10;
        }
        line.number_[line.number_.size() - 1] = remember;
        result.plus(line);
    }
    result.number_.normalize();
    result.isNegative_ = (isNegative_ ^ right.isNegative_);
    *this = result;
    return *this;
}

bool LargeNumber::operator< (LargeNumber const & right) const {
    if(isNegative_ != right.isNegative_) {
        if (isNegative_) return true;
        return false;
    }
    if(number_.size() < right.number_.size()) return true;
    if(number_.size() > right.number_.size()) return false;
    for(int i = number_.size() - 1; i >= 0; --i) {
        if(number_[i] < right.number_[i]) return true;
        if(number_[i] > right.number_[i]) return false;
    }
    return false;
}

bool LargeNumber::operator> (LargeNumber const & right) const {
    return ( right < *this );
}

LargeNumber & LargeNumber::plus(LargeNumber right) {
    size_t min_size = std::min(number_.size(), right.number_.size());
    if(number_.size() == min_size && number_.size() != right.number_.size()) {
        right.swap(*this);
    }
    if(number_.isSmall()) {
        unsigned sum = number_[0] + right.number_[0];
        number_[0] = sum;
        return *this;
    }
    int remember = 0;
    for(size_t i = 0; i < right.number_.size(); ++i) {
        int res = number_[i] + right.number_[i] + remember;
        number_[i] = res % 10;
        remember = res / 10;
    }
    if(remember != 0) {
        if(number_.size() == right.number_.size()) {
            number_.resize(number_.size() + 1);
        }
        for(size_t i = right.number_.size(); i < number_.size(); ++i) {
            unsigned res = number_[i] + remember;
            number_[i] = res % 10;
            remember = res / 10;
        }
        if(remember != 0) {
            number_.resize(number_.size() + 1);
            number_[number_.size() - 1] = remember;
        }
    }
    return * this;
}

LargeNumber & LargeNumber::minus(LargeNumber right) {
    if( abs(*this) < abs(right)) {
        right.swap(*this);
    }
    if(number_.isSmall() != right.number_.isSmall()) {
        right.number_.normalize();
    }
    int remember = 0;
    for(size_t i = 0; i < right.number_.size(); ++i) {
        int subtrahend = right.number_[i] + remember;
        int value = number_[i];
        minusIter(value, subtrahend, remember);
        number_[i] = value;
    }
    size_t i = 0;
    while (remember != 0) {
        int subtrahend = remember;
        int value = number_[right.number_.size() + i];
        minusIter(value, subtrahend, remember);
        number_[right.number_.size() + i] = value;
        ++i;
    }
    if(!number_.isSmall()) number_.normalize();
    return * this;
}

void LargeNumber::minusIter(int & minuend, int const & subtrahend, int & remember) {
    int res = minuend - subtrahend;
    if(res < 0) {
         remember = 1;
         res += 10;
    } else {
         remember = 0;
    }
    minuend = res;
}

void LargeNumber::swap(LargeNumber& right) {
    std::swap(isNegative_, right.isNegative_);
    std::swap(number_, right.number_);
}

LargeNumber operator+(LargeNumber left, LargeNumber const & right) {
    return (left += right);
}
LargeNumber operator-(LargeNumber left, LargeNumber const & right) {
    return (left -= right);
}
LargeNumber operator*(LargeNumber left, LargeNumber const & right) {
    return (left *= right);
}

istream & operator >>(istream & in, LargeNumber & number) {
    std::string str;
    in >> str;
    std::string const & num = (str.at(0) == '-' || str.at(0) == '+') ? str.substr(1, -1) : str;
    number.isNegative_ = (str.at(0) == '-');
    bool isCorrect = true;
    for(size_t i = 0; i < num.size(); ++i) {
        char symbol = num.at(i);
        if(symbol < '0' || symbol > '9') {
            isCorrect = false;
            break;
        }
    }
    if(isCorrect) {
        number.number_ = NumArray(num);
    } else {
        number.number_ = NumArray(0);
        number.isNegative_ = false;
    }
    return in;
}

ostream & operator <<(ostream & out, LargeNumber const & number) {
    if(number.isNegative_) {
        out << "-";
    }
    out << number.number_;
    return out;
}

LargeNumber abs(LargeNumber const & number) {
    if(number.isNegative_) {
        LargeNumber answer(number);
        answer.isNegative_ = false;
        return answer;
    }
    return number;
}